﻿package com.jfdesgagne.traklib.tracker {
	import com.jfdesgagne.traklib.colour.TrakColourFinder;
	import com.jfdesgagne.traklib.events.TrakColorEvent;

	import flash.display.BitmapData;
	import flash.display.DisplayObjectContainer;
	import flash.events.KeyboardEvent;
	import flash.events.TimerEvent;
	import flash.filters.BlurFilter;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import flash.media.Video;
	import flash.ui.Keyboard;
	import flash.utils.Timer;

	public class TrakColor extends AbstractTrak {
		public static var CALL_CHANGE_COLOR:String = "trakcolor_changecolor";
		public static var CALL_ADD_COLOR:String = "trakcolor_addcolor";
		protected var __timer:Timer;
		protected var __timeToCheck:int = 3;
		protected var __currentTimeToCheck:int = 1;
		protected var __checkedColors:Array;
		protected var __colorTracked:uint;		protected var __colorsTracked:Array = [];
		protected var __aRed:Array;
		protected var __aGreen:Array;
		protected var __aBlue:Array;
		protected var __colorSensitivity:Number = 0;
		protected var __multipleColors:Boolean;

		public function TrakColor(_input:Video, _refStage:DisplayObjectContainer):void {
			super(_input, _refStage);
			this.__refStage.addEventListener(KeyboardEvent.KEY_DOWN, this.keyboardPressed);
			this.__controlMaxUnionSize = true;
		}

		override public function set input(_v:Video):void {
			this.__input = _v;
			if ( this.__nowFrame != null ) { 
				this.__nowFrame.dispose(); 
				this.__oldFrame.dispose(); 				this.__difFrame.dispose(); 
			}
			this.__oldFrame = new BitmapData(_v.width, _v.height, true, 0);
			this.__nowFrame = new BitmapData(_v.width, _v.height, false, 0);			this.__difFrame = new BitmapData(_v.width, _v.height, false, 0);
		}
		
		protected function tresholdColor(_bmp:BitmapData):BitmapData {
			_bmp = _bmp.clone();
			
			var rmin:Number = Math.min(255, Math.max(0, ((this.__colorTracked >> 16) & 0xff) + this.__colorSensitivity));
			var gmin:Number = Math.min(255, Math.max(0, ((this.__colorTracked >> 8) & 0xff) + this.__colorSensitivity));
			var bmin:Number = Math.min(255, Math.max(0, (this.__colorTracked & 0xff) + this.__colorSensitivity));
			var rgbMin:Number = rmin << 16 | gmin << 8 | bmin;
			
			var rmax:Number = Math.min(255, Math.max(0, ((this.__colorTracked >> 16) & 0xff) - this.__colorSensitivity));
			var gmax:Number = Math.min(255, Math.max(0, ((this.__colorTracked >> 8) & 0xff ) - this.__colorSensitivity));
			var bmax:Number = Math.min(255, Math.max(0, (this.__colorTracked & 0xff) - this.__colorSensitivity));
			var rgbMax:Number = rmax << 16 | gmax << 8 | bmax;
			
			rmin = (rgbMin >> 16) & 0xff;
			gmin = (rgbMin >> 8) & 0xff;
			bmin = rgbMin & 0xff;
			
			rmax = (rgbMax >> 16) & 0xff;
			gmax = (rgbMax >> 8) & 0xff;
			bmax = rgbMax & 0xff;
			
			var tmp:Number;
			
			if (rmin > rmax) {
				tmp = rmin;
				rmin = rmax;
				rmax = tmp;
			} if (gmin > gmax) {
				tmp = gmin;
				gmin = gmax;
				gmax = tmp;
			} if (bmin > bmax) {
				tmp = bmin;
				bmin = bmax;
				bmax = tmp;
			}
			
			var zero:Point = new Point();
			//this.__nowFrame.applyFilter( this.__nowFrame, this.__nowFrame.rect, new Point(), this.__blurFilter );
			
			
			_bmp.threshold(this.__oldFrame, this.__oldFrame.rect, zero, "<", rmin << 16, 0, 0x00FF0000, true);
			
			
			//this.__nowFrame.applyFilter(this.__nowFrame, this.__nowFrame.rect, new Point(), this.__colourMatrixFilter);
			_bmp.threshold(this.__nowFrame, this.__oldFrame.rect, zero, "<", gmin << 8, 0, 0x0000ff00, true);
			_bmp.threshold(this.__nowFrame, this.__oldFrame.rect, zero, "<", bmin, 0, 0x000000ff, true);
			
			_bmp.threshold(this.__nowFrame, this.__oldFrame.rect, zero, ">", rmax << 16, 0, 0x00FF0000, true);
			_bmp.threshold(this.__nowFrame, this.__oldFrame.rect, zero, ">", gmax << 8, 0, 0x0000ff00, true);
			_bmp.threshold(this.__nowFrame, this.__oldFrame.rect, zero, ">", bmax, 0, 0x000000ff, true);
			_bmp.threshold(this.__nowFrame, this.__oldFrame.rect, zero, "==", 0xff000000, 0xffffffff, 0xff000000, true);
			
			return _bmp;	
		}

		override public function track():void {
			if(this.__colorTracked) {
				if(this.changed) {
					this.__oldFrame.draw(this.__input, this.__matrix);
					super.applyFilters();
					
					
					// TODO: duplicate  the current bipmap data to do multiple color detection
					if(this.__multipleColors) {
						for(var i:int=0; i<this.__colorsTracked.length; i++) {
							this.__colorTracked = this.__colorsTracked[i];
							this.__nowFrame = this.tresholdColor(this.__nowFrame);
						}
					} else {
						this.__nowFrame = this.tresholdColor(this.__nowFrame);
					}
					
					
					this.__nowFrame.applyFilter(this.__nowFrame, this.__oldFrame.rect, new Point(), new BlurFilter(5, 5));
				
					this.shrinkMask(4);
					this.growMask(3);
					
					
					super.track();
				}
			}
		}
		

		protected function growMask(_val:int):void {
			var tinyBlur:BlurFilter = new BlurFilter(1.5, 1.5, 3);
			for (var i:int = 0;i < _val;i++) {
				this.__nowFrame.applyFilter(this.__nowFrame, this.__oldFrame.rect, new Point(), tinyBlur);
				this.__nowFrame.threshold(this.__nowFrame, this.__oldFrame.rect, new Point(), "!=", 0x00000000, 0xffffffff, 0xff000000, true);
			}
		}

		protected function shrinkMask(_val:int):void {
			var tinyBlur:BlurFilter = new BlurFilter(1.5, 1.5, 3);
			for (var i:int = 0;i < _val;i++) {
				this.__nowFrame.applyFilter(this.__nowFrame, this.__oldFrame.rect, new Point(), tinyBlur);
				this.__nowFrame.threshold(this.__nowFrame, this.__oldFrame.rect, new Point(), "<", 0xe0000000, 0x00000000, 0xff000000, true);
				this.__nowFrame.threshold(this.__nowFrame, this.__oldFrame.rect, new Point(), "!=", 0x00000000, 0xffffffff, 0xff000000, true);
			}
		}

		protected function keyboardPressed(e:KeyboardEvent):void {
			if(e.keyCode == Keyboard.SPACE) {
				this.changeColor();
			}
		}
		
		public function addColor():void {
			this.__multipleColors = true;
			this.startDetectColor();
		}
		
		protected function startDetectColor():void {
			if(this.__timer) {
				this.__timer.stop();
				this.__timer.removeEventListener(TimerEvent.TIMER, this.checkColor);
				this.__timer = null;
			}
			
			this.__currentTimeToCheck = 1;
			this.__checkedColors = new Array();
		
			this.__timer = new Timer(1000, 0);
			this.__timer.addEventListener(TimerEvent.TIMER, this.checkColor);
			this.__timer.start();
			
			this.dispatchEvent(new TrakColorEvent(TrakColorEvent.COUNTDOWN, this.__currentTimeToCheck));
			
		}
		
		public function changeColor():void {
			this.__colorsTracked = [];
			this.__multipleColors = false;
			this.startDetectColor();
		}

		protected function checkColor(e:TimerEvent):void {
			this.__currentTimeToCheck++;
				
			if(this.__currentTimeToCheck > this.__timeToCheck) {
				this.__timer.stop();
				this.__timer.removeEventListener(TimerEvent.TIMER, this.checkColor);
				this.__timer = null;
				
				var inputBmp:BitmapData = new BitmapData(this.__input.width, this.__input.height, false);
				inputBmp.draw(this.__input);
				
				var checkAgerageColor:BitmapData = new BitmapData(50, 50);
				checkAgerageColor.copyPixels(inputBmp, new Rectangle(0, inputBmp.height - 50, 50, 50), new Point(0, 0));
				checkAgerageColor.applyFilter(checkAgerageColor, checkAgerageColor.rect, new Point(), this.__colourMatrixFilter);
					
				TrakColourFinder.reduceColours(checkAgerageColor);
				
				this.__colorTracked = TrakColourFinder.averageColour(checkAgerageColor);
				
				//TODO: detect outlier
				if(this.__multipleColors) {
					this.__colorsTracked.push(this.__colorTracked);
				}			
				
				checkAgerageColor.dispose();
				checkAgerageColor = null;
				
				inputBmp.dispose();
				inputBmp = null;
				
				this.dispatchEvent(new TrakColorEvent(TrakColorEvent.COUNTDOWN, 0));
				this.dispatchEvent(new TrakColorEvent(TrakColorEvent.COLOR_CHANGED, this.__colorTracked));
			} else {
				this.dispatchEvent(new TrakColorEvent(TrakColorEvent.COUNTDOWN, this.__currentTimeToCheck));
			}
		}

		public function set colorSensitivity(_colorSensitivity:Number):void {
			this.__colorSensitivity = _colorSensitivity;
		}
	}
}
